﻿namespace FastColoredTextBoxNS
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Drawing2D;

    /// <summary>
    /// 	Base class for bookmark collection
    /// </summary>
    public abstract class BaseBookmarks : ICollection<Bookmark>, IDisposable
    {
        #region ICollection

        public abstract void Add(Bookmark item);

        public abstract void Clear();

        public abstract bool Contains(Bookmark item);

        public abstract void CopyTo(Bookmark[] array, int arrayIndex);

        public abstract int Count { get; }

        public abstract bool IsReadOnly { get; }

        public abstract bool Remove(Bookmark item);

        public abstract IEnumerator<Bookmark> GetEnumerator();

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion

        #region IDisposable

        public abstract void Dispose();

        #endregion

        #region Additional properties

        public abstract void Add(int lineIndex, string bookmarkName);

        public abstract void Add(int lineIndex);

        public abstract bool Contains(int lineIndex);

        public abstract bool Remove(int lineIndex);

        public abstract Bookmark GetBookmark(int i);

        #endregion
    }

    /// <summary>
    /// 	Collection of bookmarks
    /// </summary>
    public class Bookmarks : BaseBookmarks
    {
        protected FastColoredTextBox tb;

        protected List<Bookmark> items = new List<Bookmark>();

        protected int counter;

        public Bookmarks(FastColoredTextBox tb)
        {
            this.tb = tb;
            tb.LineInserted += this.tb_LineInserted;
            tb.LineRemoved += this.tb_LineRemoved;
        }

        protected virtual void tb_LineRemoved(object sender, LineRemovedEventArgs e)
        {
            for (int i = 0; i < this.Count; i++)
            {
                if (this.items[i].LineIndex >= e.Index)
                {
                    if (this.items[i].LineIndex >= e.Index + e.Count)
                    {
                        this.items[i].LineIndex = this.items[i].LineIndex - e.Count;
                        continue;
                    }
                    if (this.items[i].LineIndex == e.Index + e.Count - 1)
                    {
                        this.items[i].LineIndex = this.items[i].LineIndex - e.Count;
                        continue;
                    }
                    this.items.RemoveAt(i);
                    i--;
                }
            }
        }

        protected virtual void tb_LineInserted(object sender, LineInsertedEventArgs e)
        {
            for (int i = 0; i < this.Count; i++)
            {
                if (this.items[i].LineIndex >= e.Index)
                {
                    this.items[i].LineIndex = this.items[i].LineIndex + e.Count;
                }
                else if (this.items[i].LineIndex == e.Index - 1 && e.Count == 1)
                {
                    if (this.tb[e.Index - 1].StartSpacesCount == this.tb[e.Index - 1].Count)
                    {
                        this.items[i].LineIndex = this.items[i].LineIndex + e.Count;
                    }
                }
            }
        }

        public override void Dispose()
        {
            this.tb.LineInserted -= this.tb_LineInserted;
            this.tb.LineRemoved -= this.tb_LineRemoved;
        }

        public override IEnumerator<Bookmark> GetEnumerator()
        {
            foreach (var item in this.items)
            {
                yield return item;
            }
        }

        public override void Add(int lineIndex, string bookmarkName)
        {
            this.Add(new Bookmark(this.tb, bookmarkName ?? "Bookmark " + this.counter, lineIndex));
        }

        public override void Add(int lineIndex)
        {
            this.Add(new Bookmark(this.tb, "Bookmark " + this.counter, lineIndex));
        }

        public override void Clear()
        {
            this.items.Clear();
            this.counter = 0;
        }

        public override void Add(Bookmark bookmark)
        {
            foreach (var bm in this.items)
            {
                if (bm.LineIndex == bookmark.LineIndex)
                {
                    return;
                }
            }

            this.items.Add(bookmark);
            this.counter++;
            this.tb.Invalidate();
        }

        public override bool Contains(Bookmark item)
        {
            return this.items.Contains(item);
        }

        public override bool Contains(int lineIndex)
        {
            foreach (var item in this.items)
            {
                if (item.LineIndex == lineIndex)
                {
                    return true;
                }
            }
            return false;
        }

        public override void CopyTo(Bookmark[] array, int arrayIndex)
        {
            this.items.CopyTo(array, arrayIndex);
        }

        public override int Count
        {
            get
            {
                return this.items.Count;
            }
        }

        public override bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        public override bool Remove(Bookmark item)
        {
            this.tb.Invalidate();
            return this.items.Remove(item);
        }

        /// <summary>
        /// 	Removes bookmark by line index
        /// </summary>
        public override bool Remove(int lineIndex)
        {
            bool was = false;
            for (int i = 0; i < this.Count; i++)
            {
                if (this.items[i].LineIndex == lineIndex)
                {
                    this.items.RemoveAt(i);
                    i--;
                    was = true;
                }
            }
            this.tb.Invalidate();

            return was;
        }

        /// <summary>
        /// 	Returns Bookmark by index.
        /// </summary>
        public override Bookmark GetBookmark(int i)
        {
            return this.items[i];
        }
    }

    /// <summary>
    /// 	Bookmark of FastColoredTextbox
    /// </summary>
    public class Bookmark
    {
        public FastColoredTextBox TB { get; private set; }

        /// <summary>
        /// 	Name of bookmark
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 	Line index
        /// </summary>
        public int LineIndex { get; set; }

        /// <summary>
        /// 	Color of bookmark sign
        /// </summary>
        public Color Color { get; set; }

        /// <summary>
        /// 	Scroll textbox to the bookmark
        /// </summary>
        public virtual void DoVisible()
        {
            this.TB.Selection.Start = new Place(0, this.LineIndex);
            this.TB.DoRangeVisible(this.TB.Selection, true);
            this.TB.Invalidate();
        }

        public Bookmark(FastColoredTextBox tb, string name, int lineIndex)
        {
            this.TB = tb;
            this.Name = name;
            this.LineIndex = lineIndex;
            this.Color = tb.BookmarkColor;
        }

        public virtual void Paint(Graphics gr, Rectangle lineRect)
        {
            int size = this.TB.CharHeight - 1;
            using (
                var brush = new LinearGradientBrush(
                    new Rectangle(0, lineRect.Top, size, size), Color.White, this.Color, 45)) gr.FillEllipse(brush, 0, lineRect.Top, size, size);
            using (var pen = new Pen(this.Color)) gr.DrawEllipse(pen, 0, lineRect.Top, size, size);
        }
    }
}